package org.zoomdev.zoom.aop.impl;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import org.zoomdev.zoom.aop.Invoker;
import org.zoomdev.zoom.aop.MethodInvokerFactory;
import org.zoomdev.zoom.aop.factory.impl.AsmSingleMethodFactory;
import org.zoomdev.zoom.aop.utils.AopUtils;
import org.zoomdev.zoom.aop.utils.AsmUtils;
import org.zoomdev.zoom.common.caster.Caster;
import org.zoomdev.zoom.common.utils.DigestUtils;


public final class MethodInvokerFactoryImpl extends AsmSingleMethodFactory<Invoker, java.lang.reflect.Method> implements MethodInvokerFactory {


    public MethodInvokerFactoryImpl(ClassLoader classLoader) {
        super(classLoader);
    }

    @Override
    protected String getClassName(java.lang.reflect.Method param) {
        return param.getDeclaringClass().getName() + "." + param.getName() +
                DigestUtils.md5Hex(param.toGenericString()).substring(8, 24);
    }


    private byte[] generateBytecode(Class<?> declaringClass, java.lang.reflect.Method method, String className) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);

        String superName = Type.getInternalName(Object.class);

        // Class header
        cw.visit(AsmUtils.VERSION, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
                className,
                null,
                superName,
                new String[]{Type.getInternalName(Invoker.class)});

        // 添加方法签名字段（用于生成稳定的hashCode）


        addStaticInfo(cw, declaringClass, method, className);
        ///////////////////////////

        // Default constructor
        AsmUtils.generateConstructor(cw, superName);

        // Implement invoke method
        generateInvokeMethod(cw, declaringClass, method);


        // 添加toString方法
        generateToStringMethod(cw, className);


        // 添加hashCode方法
        generateHashCodeMethod(cw, className);

        cw.visitEnd();
        return cw.toByteArray();
    }

    private void addStaticInfo(ClassWriter cw, Class<?> declaringClass, java.lang.reflect.Method method, String internalName) {
        // 添加方法信息字段
        // 添加方法信息字段
        FieldVisitor fv = cw.visitField(
                Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC,
                "METHOD_INFO",
                "Ljava/lang/String;",
                null,
                null
        );
        fv.visitEnd();

        // 添加方法哈希字段
        FieldVisitor hashFv = cw.visitField(
                Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC,
                "METHOD_HASH",
                "I",
                null,
                null
        );
        hashFv.visitEnd();

        // 静态初始化块 - 设置方法信息和哈希值
        MethodVisitor mv = cw.visitMethod(
                Opcodes.ACC_STATIC,
                "<clinit>",
                "()V",
                null,
                null
        );
        mv.visitCode();

        // 1. 构建方法信息字符串
        buildMethodInfoString(mv, declaringClass, method);
        mv.visitFieldInsn(Opcodes.PUTSTATIC, internalName, "METHOD_INFO", "Ljava/lang/String;");

        // 2. 计算并设置方法哈希值
        // 计算稳定的哈希值（基于方法签名）
        int methodHash = AopUtils.calculateMethodHash(declaringClass, method);
        mv.visitLdcInsn(methodHash);
        mv.visitFieldInsn(Opcodes.PUTSTATIC, internalName, "METHOD_HASH", "I");

        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 0); // COMPUTE_MAXS 会自动计算
        mv.visitEnd();
    }

    // 构建方法信息字符串
    private void buildMethodInfoString(MethodVisitor mv, Class<?> declaringClass, java.lang.reflect.Method method) {
        // 创建 StringBuilder
        mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
        mv.visitInsn(Opcodes.DUP);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);

//        // 添加类名
//        mv.visitLdcInsn("Method: ");
//        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        Class orgClass = AopUtils.getOriginalClass(declaringClass);
        // 添加声明类名
        mv.visitLdcInsn(orgClass.getSimpleName());
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 添加分隔符
        mv.visitLdcInsn("#");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 添加方法名
        String name = method.getName();
        if (name.startsWith("__")) {
            name = name.substring(2);
        }
        mv.visitLdcInsn(name);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 添加参数列表
        mv.visitLdcInsn("(");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 添加参数类型
        Class<?>[] paramTypes = method.getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            if (i > 0) {
                mv.visitLdcInsn(", ");
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            }
            mv.visitLdcInsn(paramTypes[i].getSimpleName());
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
        }

        // 结束参数列表
        mv.visitLdcInsn(")");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 添加返回类型
//        mv.visitLdcInsn(" : ");
//        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
//        mv.visitLdcInsn(method.getReturnType().getSimpleName());
//        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 转换为字符串
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
    }

    // 生成 toString 方法
    private void generateToStringMethod(ClassWriter cw, String className) {
        MethodVisitor mv = cw.visitMethod(
                Opcodes.ACC_PUBLIC,
                "toString",
                "()Ljava/lang/String;",
                null,
                null
        );
        mv.visitCode();

        // 创建 StringBuilder 实例
        mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
        mv.visitInsn(Opcodes.DUP);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);

        // 添加前缀
        mv.visitLdcInsn("GeneratedInvoker[");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 加载静态字段 METHOD_INFO
        mv.visitFieldInsn(Opcodes.GETSTATIC, className, "METHOD_INFO", "Ljava/lang/String;");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 添加后缀
        mv.visitLdcInsn("]");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

        // 转换为字符串并返回
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
        mv.visitInsn(Opcodes.ARETURN);

        // 自动计算最大栈大小和局部变量表
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    // 生成 toString 方法


    private void generateInvokeMethod(ClassWriter cw, Class<?> declaringClass, java.lang.reflect.Method method) {
        Method asmMethod = Method.getMethod("Object invoke(Object,Object[])");
        GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, asmMethod, null, null, cw);

        // Method body
        mg.visitCode();

        // Cast target object
        mg.visitVarInsn(Opcodes.ALOAD, 1);
        mg.checkCast(Type.getType(declaringClass));

        // Prepare parameters
        Class<?>[] paramTypes = method.getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            mg.visitVarInsn(Opcodes.ALOAD, 2);
            mg.visitIntInsn(Opcodes.BIPUSH, i);
            mg.visitInsn(Opcodes.AALOAD);
            generateParameterConversion(mg, paramTypes[i]);
        }

        // Invoke target method
        if (declaringClass.isInterface()) {
            mg.visitMethodInsn(
                    Opcodes.INVOKEINTERFACE,
                    Type.getInternalName(declaringClass),
                    method.getName(),
                    Type.getMethodDescriptor(method),
                    true);
        } else {
            mg.visitMethodInsn(
                    Opcodes.INVOKEVIRTUAL,
                    Type.getInternalName(declaringClass),
                    method.getName(),
                    Type.getMethodDescriptor(method),
                    false);
        }

        // Handle return value
        generateReturnValue(mg, Type.getType(method.getReturnType()));

        mg.endMethod();
    }

    private void generateParameterConversion(GeneratorAdapter mg, Class<?> paramType) {
        Type type = Type.getType(paramType);
        if (paramType.isPrimitive()) {
            Type wrapper = Type.getType(Caster.getWrapClass(paramType));
            mg.checkCast(wrapper);
            mg.unbox(type);
        } else if (paramType.isArray()) {
            mg.checkCast(type);
        } else {
            mg.checkCast(type);
        }
    }

    private void generateReturnValue(GeneratorAdapter mg, Type returnType) {
        if (returnType.equals(Type.VOID_TYPE)) {
            mg.visitInsn(Opcodes.ACONST_NULL);
        } else if (returnType.getSort() == Type.OBJECT) {
            mg.checkCast(Type.getType(Object.class));
        } else {
            mg.box(returnType);
        }
        mg.visitInsn(Opcodes.ARETURN);
    }


    @Override
    protected byte[] createBytes(String className, java.lang.reflect.Method param) {
        return generateBytecode(param.getDeclaringClass(), param, className);
    }


    // 生成稳定的hashCode方法
    private void generateHashCodeMethod(ClassWriter cw, String internalName) {
        MethodVisitor mv = cw.visitMethod(
                Opcodes.ACC_PUBLIC,
                "hashCode",
                "()I",
                null,
                null
        );
        mv.visitCode();

        // 直接返回静态字段 METHOD_HASH 的值
        mv.visitFieldInsn(Opcodes.GETSTATIC,
                internalName,
                "METHOD_HASH",
                "I");

        mv.visitInsn(Opcodes.IRETURN);

        // 设置最大栈和局部变量表
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }
}
